(ns cljfx.dsl
  (:import [javafx.scene DepthTest Parent]
           [javafx.scene.control Button Label ToolBar ToggleButton ToggleGroup SplitPane TreeView ScrollPane TextField ContextMenu]
           [javafx.scene.image ImageView]
           [javafx.scene.layout AnchorPane TilePane BorderPane Pane Region StackPane HBox Priority GridPane HBox VBox]
           [javafx.event EventHandler]
           [javafx.beans.value ChangeListener]
           [javafx.beans InvalidationListener]
           [clojure.lang ISeq]))

(defn capitalize [s]
  (apply str (cons (Character/toUpperCase (first s)) (rest s))))

(defmacro defbuilder [sym]
  `(defn ~sym [& attrs#]
     (let [instance# (~(symbol (str (capitalize (name sym)) ".")))]
       (doseq [attr# attrs#] (attr# instance#))
       instance#)))

(defmacro defbuilders [& builders]
  (cons `do (for [builder builders] `(defbuilder ~(symbol (name builder))))))

(defbuilders button anchorPane label borderPane stackPane toolBar toggleButton toggleGroup splitPane treeView
  imageView vBox hBox scrollPane tilePane pane textField contextMenu)

(defmacro defattr [sym]
  `(defn ~sym
     ([a1#] #(do (~(symbol (str ".set" (capitalize (name sym)))) % a1#) %))
     ([a1# a2#] #(do (~(symbol (str ".set" (capitalize (name sym)))) % a1# a2#) %))))

;(defmacro defattr [sym]
;  `(defn ~sym [val#]
;     #(do (~(symbol (str ".set" (capitalize (name sym)))) % val#) %)))

(defmacro defattrs [& attrs]
  (cons `do (for [attr attrs] `(defattr ~(symbol (name attr))))))

(defattrs text prefWidth prefHeight focusTraversable id visible depthTest top center graphic minSize prefSize spacing
  maxSize dividerPosition minHeight minWidth maxWidth maxHeight editable showRoot root selected
  fitToWidth content hgap vgap prefColumns promptText
  )


(defprotocol AsChild
  (append-to-children [this parent]))

(extend-protocol AsChild
  javafx.scene.Node
  (append-to-children [this parent]
    (.. parent getChildren (add this)))
  ISeq
  (append-to-children [this parent]
    (.. parent getChildren (addAll this)))
  )

(defn children [& childs]
  (fn [comp]
    (doseq [child (remove nil? childs)] (append-to-children child comp))
    comp))

(defn items [& itms]
  #(do (.. % getItems (addAll itms)) %))

(defn on-action [f]
  #(.setOnAction % (reify EventHandler
                     (handle [this evt] (f evt)))))

(defn on-key-released [f]
  #(.setOnKeyReleased % (reify EventHandler
                          (handle [this evt] (f this evt)))))

;(defn selected-property-invalidated [f]
;  #(.. % selectedProperty (addListener (reify InvalidationListener
;                                         (invalidated [this ov] (f ov))))))

;(defn on-selected-toggle-changed [f]
;  #(.. % selectedToggleProperty (addListener (reify ChangeListener
;                                               (changed [this ov old new] (f ov old new))))))

(defmacro property-changed [property f]
  `(fn [comp#]
     (.. comp# ~(symbol (str (name property) "Property"))
       (addListener (reify ChangeListener
                      (changed [this# ov# old# new#] (~f this# ov# old# new#)))))))

(defmacro property-invalidated [property f]
  `(fn [comp#]
     (.. comp# ~(symbol (str property "Property"))
       (addListener (reify InvalidationListener
                      (invalidated [this# ov#] (~f this# ov#)))))))

(defn on-mouse-clicked [f]
  #(.setOnMouseClicked % (reify EventHandler
                           (handle [this evt] (f evt)))))

(defmacro defas [var]
  `#(def ~var %))

(defn styleClass [& classes]
  #(.. % getStyleClass (addAll classes)))

(defn spacer []
  (let [reg (Region.)]
    (HBox/setHgrow reg Priority/ALWAYS)
    reg))

;(defn gridpane-constraints [& args]
;  (fn [comp]
;    (let [partial-setter (partial #(GridPane/setConstraints %&) comp)] ; not work
;      (partial-setter args)
;      comp)))

(defn gridpane-constraints
  ([col row] #(do (GridPane/setConstraints % col row) %))
  ([col row colspan rowspan] #(do (GridPane/setConstraints % col row colspan rowspan) %))
  ([col row colspan rowspan halign valign] #(do (GridPane/setConstraints % col row colspan rowspan halign valign) %)))

(defn in-toggle-group [group]
  #(.setToggleGroup % group))

(defn selectionMode [model]
  #(.. % getSelectionModel (setSelectionMode model)))